﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Validity.Test
{
  [TestClass]
  public class ComparableNullableTest
  {
    [TestMethod]
    public void TestHasValue()
    {
      Assert.IsFalse(new ComparableNullable<DateTime>().HasValue);
      Assert.IsFalse(new ComparableNullable<DateTime>(new Nullable<DateTime>()).HasValue);
      Assert.IsTrue(new ComparableNullable<DateTime>(DateTime.Today).HasValue);
      Assert.IsTrue(new ComparableNullable<DateTime>(new Nullable<DateTime>(DateTime.Today)).HasValue);
    }

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void TestValueThrowsIfHasValueFalse()
    {
      Assert.IsNull(new ComparableNullable<DateTime>().Value);
    }

    [TestMethod]
    public void TestValue()
    {
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(DateTime.Today).Value);
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(new Nullable<DateTime>(DateTime.Today)).Value);
    }

    [TestMethod]
    public void TestGetValueOrDefault()
    {
      Assert.AreEqual(DateTime.MinValue, new ComparableNullable<DateTime>().GetValueOrDefault());
      Assert.AreEqual(DateTime.MinValue, new ComparableNullable<DateTime>(new Nullable<DateTime>()).GetValueOrDefault());
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(DateTime.Today).GetValueOrDefault());
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(new Nullable<DateTime>(DateTime.Today)).GetValueOrDefault());
      Assert.AreEqual(DateTime.MaxValue, new ComparableNullable<DateTime>().GetValueOrDefault(DateTime.MaxValue));
      Assert.AreEqual(DateTime.MaxValue, new ComparableNullable<DateTime>(new Nullable<DateTime>()).GetValueOrDefault(DateTime.MaxValue));
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(DateTime.Today).GetValueOrDefault(DateTime.MaxValue));
      Assert.AreEqual(DateTime.Today, new ComparableNullable<DateTime>(new Nullable<DateTime>(DateTime.Today)).GetValueOrDefault(DateTime.MaxValue));
    }

    [TestMethod]
    public void TestGetHashCode()
    {
      Assert.AreEqual(0, new ComparableNullable<DateTime>().GetHashCode());
      Assert.AreEqual(0, new ComparableNullable<DateTime>(new Nullable<DateTime>()).GetHashCode());
      Assert.AreEqual(1982421132, new ComparableNullable<DateTime>(new DateTime(2010, 01, 01)).GetHashCode());
      Assert.AreEqual(1982421132, new ComparableNullable<DateTime>(new DateTime(2010, 01, 01)).GetHashCode());
    }

    [TestMethod]
    public void TestToString()
    {
      Assert.AreEqual(String.Empty, new ComparableNullable<DateTime>().ToString());
      Assert.AreEqual(String.Empty, new ComparableNullable<DateTime>(new Nullable<DateTime>()).ToString());
      Assert.AreEqual(new DateTime(2010, 01, 01).ToString(), new ComparableNullable<DateTime>(new DateTime(2010, 01, 01)).ToString());
      Assert.AreEqual(new DateTime(2010, 01, 01).ToString(), new ComparableNullable<DateTime>(new DateTime(2010, 01, 01)).ToString());
    }

    [TestMethod]
    public void TestExplicitOperator()
    {
      Assert.AreEqual(DateTime.Today, (DateTime)new ComparableNullable<DateTime>(DateTime.Today));
      Assert.AreEqual(new Nullable<DateTime>(DateTime.Today), (Nullable<DateTime>)new ComparableNullable<DateTime>(DateTime.Today));
    }

    [TestMethod]
    public void TestImplicitOperator()
    {
      ComparableNullable<DateTime> d1 = DateTime.Today;
      ComparableNullable<DateTime> d2 = new Nullable<DateTime>(DateTime.Today);
    }

    [TestMethod]
    public void TestEquals()
    {
      var hasValue = new ComparableNullable<DateTime>(DateTime.Today);
      var noValue = new ComparableNullable<DateTime>();

      Assert.IsFalse(hasValue.Equals(null));
      Assert.IsTrue(hasValue.Equals(DateTime.Today));
      Assert.IsFalse(hasValue.Equals(new Nullable<DateTime>()));
      Assert.IsTrue(hasValue.Equals(new Nullable<DateTime>(DateTime.Today)));
      Assert.IsFalse(hasValue.Equals(new ComparableNullable<DateTime>()));
      Assert.IsTrue(hasValue.Equals(new ComparableNullable<DateTime>(DateTime.Today)));
      Assert.IsFalse(hasValue.Equals(new ComparableNullable<DateTime>(DateTime.Today.AddDays(1))));

      Assert.IsTrue(noValue.Equals(null));
      Assert.IsFalse(noValue.Equals(DateTime.Today));
      Assert.IsTrue(noValue.Equals(new Nullable<DateTime>()));
      Assert.IsFalse(noValue.Equals(new Nullable<DateTime>(DateTime.Today)));
      Assert.IsTrue(noValue.Equals(new ComparableNullable<DateTime>()));
      Assert.IsFalse(noValue.Equals(new ComparableNullable<DateTime>(DateTime.Today)));
      Assert.IsFalse(noValue.Equals(new ComparableNullable<DateTime>(DateTime.Today.AddDays(1))));
    }

    [TestMethod]
    public void TestCompareTo()
    {
      var minDate = new ComparableNullable<DateTime>(DateTime.MinValue);
      var today = new ComparableNullable<DateTime>(DateTime.Today);
      var maxDate = new ComparableNullable<DateTime>(DateTime.MaxValue);

      Assert.AreEqual(1, minDate.CompareTo(null));
      Assert.AreEqual(0, minDate.CompareTo(DateTime.MinValue));
      Assert.AreEqual(-1, minDate.CompareTo(DateTime.Today));
      Assert.AreEqual(-1, minDate.CompareTo(DateTime.MaxValue));

      Assert.AreEqual(1, today.CompareTo(null));
      Assert.AreEqual(1, today.CompareTo(DateTime.MinValue));
      Assert.AreEqual(0, today.CompareTo(DateTime.Today));
      Assert.AreEqual(-1, today.CompareTo(DateTime.MaxValue));

      Assert.AreEqual(1, maxDate.CompareTo(null));
      Assert.AreEqual(1, maxDate.CompareTo(DateTime.MinValue));
      Assert.AreEqual(1, maxDate.CompareTo(DateTime.Today));
      Assert.AreEqual(0, maxDate.CompareTo(DateTime.MaxValue));

      Assert.AreEqual(1, ((IComparable)today).CompareTo(null));
    }

  }
}
